package com.example.redislearning.service.impl;

import com.example.redislearning.service.RateLimitService;
import com.example.redislearning.util.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 限流服务实现类
 * 基于Redis实现令牌桶和时间窗口限流算法
 * 
 * 实现原理：
 * 1. 时间窗口限流：使用Redis的INCR和EXPIRE命令
 * 2. 令牌桶限流：使用Redis的List和Lua脚本
 * 3. 滑动窗口限流：使用Redis的Sorted Set
 */
@Service
public class RateLimitServiceImpl implements RateLimitService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RedisKeyUtil redisKeyUtil;

    /**
     * 检查API调用是否被限流 - 基于时间窗口
     * @param apiKey API密钥
     * @param userId 用户ID
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 是否被限流的布尔值，true表示被限流，false表示未被限流
     */
    @Override
    public boolean isApiRateLimited(String apiKey, Integer userId, int maxRequests, int timeWindow) {
        String key = redisKeyUtil.getApiRateLimitKey(apiKey, userId);
        return checkRateLimit(key, maxRequests, timeWindow);
    }

    /**
     * 检查用户行为是否被限流 - 基于时间窗口
     * @param action 用户行为类型
     * @param userId 用户ID
     * @param maxActions 最大行为次数
     * @param timeWindow 时间窗口（秒）
     * @return 是否被限流的布尔值，true表示被限流，false表示未被限流
     */
    @Override
    public boolean isUserActionRateLimited(String action, Integer userId, int maxActions, int timeWindow) {
        String key = redisKeyUtil.getUserActionRateLimitKey(action, userId);
        return checkRateLimit(key, maxActions, timeWindow);
    }

    /**
     * 获取API剩余调用次数
     * @param apiKey API密钥
     * @param userId 用户ID
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 剩余可调用次数
     */
    @Override
    public int getApiRemainingRequests(String apiKey, Integer userId, int maxRequests, int timeWindow) {
        String key = redisKeyUtil.getApiRateLimitKey(apiKey, userId);
        return getRemainingRequests(key, maxRequests, timeWindow);
    }

    /**
     * 获取用户行为剩余次数
     * @param action 用户行为类型
     * @param userId 用户ID
     * @param maxActions 最大行为次数
     * @param timeWindow 时间窗口（秒）
     * @return 剩余可执行次数
     */
    @Override
    public int getUserActionRemaining(String action, Integer userId, int maxActions, int timeWindow) {
        String key = redisKeyUtil.getUserActionRateLimitKey(action, userId);
        return getRemainingRequests(key, maxActions, timeWindow);
    }

    /**
     * 重置API限流计数
     * @param apiKey API密钥
     * @param userId 用户ID
     * @return 是否成功重置的布尔值
     */
    @Override
    public boolean resetApiRateLimit(String apiKey, Integer userId) {
        String key = redisKeyUtil.getApiRateLimitKey(apiKey, userId);
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    /**
     * 重置用户行为限流计数
     * @param action 用户行为类型
     * @param userId 用户ID
     * @return 是否成功重置的布尔值
     */
    @Override
    public boolean resetUserActionRateLimit(String action, Integer userId) {
        String key = redisKeyUtil.getUserActionRateLimitKey(action, userId);
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    /**
     * 获取限流统计信息
     * @param apiKey API密钥
     * @param userId 用户ID
     * @return 包含限流统计信息的字符串
     */
    @Override
    public String getRateLimitInfo(String apiKey, Integer userId) {
        String key = redisKeyUtil.getApiRateLimitKey(apiKey, userId);
        String countStr = (String) redisTemplate.opsForValue().get(key);
        Long ttl = redisTemplate.getExpire(key, TimeUnit.SECONDS);
        
        return String.format("API: %s, User: %d, Current: %s, TTL: %d seconds", 
                apiKey, userId, countStr != null ? countStr : "0", ttl != null ? ttl : 0);
    }

    /**
     * 配置限流规则 - 基于令牌桶算法
     * @param apiKey API密钥
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @param burstSize 突发大小（预留容量）
     * @return 是否成功配置的布尔值
     */
    @Override
    public boolean configureRateLimit(String apiKey, int maxRequests, int timeWindow, int burstSize) {
        String key = redisKeyUtil.getTokenBucketKey(apiKey);
        
        // 初始化令牌桶
        for (int i = 0; i < maxRequests; i++) {
            redisTemplate.opsForList().leftPush(key, System.currentTimeMillis());
        }
        
        // 设置过期时间
        redisTemplate.expire(key, timeWindow, TimeUnit.SECONDS);
        return true;
    }

    /**
     * 检查IP是否被限流
     * @param ipAddress IP地址
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 是否被限流的布尔值，true表示被限流，false表示未被限流
     */
    @Override
    public boolean isIpRateLimited(String ipAddress, int maxRequests, int timeWindow) {
        String key = redisKeyUtil.getIpRateLimitKey(ipAddress);
        return checkRateLimit(key, maxRequests, timeWindow);
    }

    /**
     * 全局API限流检查
     * @param apiKey API密钥
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 是否被限流的布尔值，true表示被限流，false表示未被限流
     */
    @Override
    public boolean isGlobalApiRateLimited(String apiKey, int maxRequests, int timeWindow) {
        String key = redisKeyUtil.getGlobalApiRateLimitKey(apiKey);
        return checkRateLimit(key, maxRequests, timeWindow);
    }

    /**
     * 获取当前限流状态
     * @param apiKey API密钥
     * @param userId 用户ID
     * @return 包含限流状态的Map，包含apiKey、userId、apiCount、apiTtl、tokenCount等键值
     */
    @Override
    public Map<String, Object> getCurrentRateLimitStatus(String apiKey, Integer userId) {
        Map<String, Object> status = new HashMap<>();
        
        // API限流状态
        String apiKeyStr = redisKeyUtil.getApiRateLimitKey(apiKey, userId);
        String apiCount = (String) redisTemplate.opsForValue().get(apiKeyStr);
        Long apiTtl = redisTemplate.getExpire(apiKeyStr, TimeUnit.SECONDS);
        
        // 令牌桶状态
        String tokenKey = redisKeyUtil.getTokenBucketKey(apiKey);
        Long tokenCount = redisTemplate.opsForList().size(tokenKey);
        
        status.put("apiKey", apiKey);
        status.put("userId", userId);
        status.put("apiCount", apiCount != null ? Integer.parseInt(apiCount) : 0);
        status.put("apiTtl", apiTtl != null ? apiTtl : 0);
        status.put("tokenCount", tokenCount != null ? tokenCount : 0);
        
        return status;
    }

    /**
     * 动态调整限流阈值
     * @param apiKey API密钥
     * @param newMaxRequests 新的最大请求次数
     * @param newTimeWindow 新的时间窗口（秒）
     * @return 是否成功调整的布尔值
     */
    @Override
    public boolean adjustRateLimitThreshold(String apiKey, int newMaxRequests, int newTimeWindow) {
        String key = redisKeyUtil.getApiRateLimitKey(apiKey, null);
        // 重新设置过期时间
        redisTemplate.expire(key, newTimeWindow, TimeUnit.SECONDS);
        return true;
    }

    /**
     * 清理过期的限流数据
     * 使用Lua脚本批量清理Redis中过期的限流键
     */
    @Override
    public void cleanupExpiredRateLimitData() {
        // 使用Lua脚本批量清理过期键
        String luaScript = "local keys = redis.call('keys', 'rate_limit:*') " +
                "local count = 0 " +
                "for i=1,#keys do " +
                "    if redis.call('ttl', keys[i]) == -1 then " +
                "        redis.call('del', keys[i]) " +
                "        count = count + 1 " +
                "    end " +
                "end " +
                "return count";
        
        DefaultRedisScript<Integer> script = new DefaultRedisScript<>();
        script.setScriptText(luaScript);
        script.setResultType(Integer.class);
        
        Integer deletedCount = redisTemplate.execute(script, Arrays.asList());
        System.out.println("清理过期限流数据: " + deletedCount + " 条记录");
    }

    /**
     * 内部方法：检查限流
     * @param key Redis键名
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 是否被限流的布尔值，true表示被限流，false表示未被限流
     */
    private boolean checkRateLimit(String key, int maxRequests, int timeWindow) {
        try {
            // 使用Lua脚本实现原子操作
            String luaScript = "local current = redis.call('get', KEYS[1]) " +
                    "if not current then " +
                    "    redis.call('setex', KEYS[1], ARGV[2], 1) " +
                    "    return 0 " +
                    "elseif tonumber(current) < tonumber(ARGV[1]) then " +
                    "    redis.call('incr', KEYS[1]) " +
                    "    return 0 " +
                    "else " +
                    "    return 1 " +
                    "end";
            
            DefaultRedisScript<Integer> script = new DefaultRedisScript<>();
            script.setScriptText(luaScript);
            script.setResultType(Integer.class);
            
            Integer result = redisTemplate.execute(script, Arrays.asList(key), 
                    String.valueOf(maxRequests), String.valueOf(timeWindow));
            
            return result != null && result == 1;
        } catch (Exception e) {
            // 降级处理：允许通过
            return false;
        }
    }

    /**
     * 内部方法：获取剩余请求次数
     * @param key Redis键名
     * @param maxRequests 最大请求次数
     * @param timeWindow 时间窗口（秒）
     * @return 剩余可请求次数
     */
    private int getRemainingRequests(String key, int maxRequests, int timeWindow) {
        String countStr = (String) redisTemplate.opsForValue().get(key);
        if (countStr == null) {
            return maxRequests;
        }
        
        int currentCount = Integer.parseInt(countStr);
        return Math.max(0, maxRequests - currentCount);
    }
}